package com.ruirenyun.handler;

import com.ruirenyun.entity.base.resp.CommonRespVo;
import com.ruirenyun.entity.constant.SYS;
import com.ruirenyun.util.common.RespUtil;
import com.ruirenyun.util.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/**
 * 全局异常处理器
 *
 * @author Deng Ye
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    public static final String ERR = "GlobalExceptionHandler";

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonRespVo handle(Exception e) {
        log.error(ERR, e);
        return RespUtil.fromSys(SYS.E10002);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public CommonRespVo handle(MethodArgumentNotValidException e) {
        BindingResult exceptions = e.getBindingResult();
        if (exceptions.hasErrors()) {
            List<ObjectError> errors = exceptions.getAllErrors();
            if (!errors.isEmpty()) {
                // 这里列出了全部错误参数，按正常逻辑，只需要第一条错误即可
                Object[] arguments = errors.get(0).getArguments();
                for (Object argument : arguments) {
                    if (argument instanceof SYS) {
                        return RespUtil.fromSys(((SYS) argument));
                    }
                }
            }
        }
        log.error(ERR, e);
        return RespUtil.fromSys(SYS.VAL10001);
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseBody
    public CommonRespVo handle(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        if (!CollectionUtils.isEmpty(constraintViolations)) {
            for (ConstraintViolation<?> constraintViolation : constraintViolations) {
                Object sysErr = constraintViolation.getConstraintDescriptor().getAttributes().get("err");
                if (sysErr instanceof SYS) {
                    return RespUtil.fromSys(((SYS) sysErr));
                }
            }
        }
        log.error(ERR, e);
        return RespUtil.fromSys(SYS.VAL10001);
    }

    @ExceptionHandler(value = CommonException.class)
    @ResponseBody
    public CommonRespVo handle(CommonException e) {
        return CommonRespVo.other(SYS.valueOf(e.getMessage()).name(), SYS.valueOf(e.getMessage()).getValue());
    }

    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public CommonRespVo handle(HttpRequestMethodNotSupportedException e) {
        log.error("请求方法异常", e);
        return RespUtil.fromSys(SYS.E10008);
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseBody
    public CommonRespVo handle(MissingServletRequestParameterException e) {
        log.error("请求参数异常", e);
        return RespUtil.fromSys(SYS.E10009);
    }

    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseBody
    public CommonRespVo handle(HttpMessageNotReadableException e) {
        log.error("请求体异常", e);
        return RespUtil.fromSys(SYS.E10012);
    }

    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public CommonRespVo handle(BindException e) {
        log.error("参数校验异常", e);
        return RespUtil.fromSys(SYS.VAL10001);
    }

}
